package com.ddlx.test.binder.activity;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.NoSuchElementException;

import com.android.internal.os.BinderInternal;
import com.ddlx.test.binder.R;
import com.ddlx.test.binder.aidl.DdlxSecurityException;
import com.ddlx.test.binder.aidl.IDataSender;
import com.ddlx.test.binder.aidl.IPostCallback;
import com.ddlx.test.binder.aidl.Message;
import com.ddlx.test.binder.aidl.Result;
import com.ddlx.test.binder.service.BinderService;
import com.ddlx.test.binder.service.OtherProcBinderService;
import com.ddlx.test.binder.util.UpdateTextViewHander;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.BadParcelableException;
import android.os.Bundle;
import android.os.IBinder;
import android.os.NetworkOnMainThreadException;
import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
import android.os.TransactionTooLargeException;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity{

	@Override
	protected void onNewIntent(Intent intent) {
		Log.d("ddlx", "MainActivity.onNewIntent");
		super.onNewIntent(intent);
	}
	
	@Override
	protected void onStart() {
		Log.d("ddlx", "MainActivity.onStart");
		super.onStart();
	}
	@Override
	public boolean dispatchTouchEvent(MotionEvent ev) {
		Log.d("ddlx", "MainActivity.dispatchTouchEvent");
		return super.dispatchTouchEvent(ev);
	}
	
	@Override
	public boolean dispatchKeyEvent(KeyEvent event) {
		Log.d("ddlx", "MainActivity.dispatchKeyEvent");
		return super.dispatchKeyEvent(event);
	}
	
//	@Override
//	protected void onStart() {
	
	public UpdateTextViewHander updateTextViewHander;
	public UpdateTextViewHander updateHintHander;
	
	public boolean checkBinderConnOk() {
		if ( dataSender != null ) {
			updateTextViewHander.appendTextln("当前已经创建了binder。点击注销，才能创建一个新的binder");
			return true;
		}
		return false;
	}
	
	public boolean checkDataSenderInited() {
		if ( dataSender == null ) {
			updateTextViewHander.appendTextln("您未创建binder对象。请先创建一个binder对象。");
			return false;
		}
		return true;
	}
	
	public IDataSender getDataSender() {
		return dataSender;
	}
	
	public void initTextInfo(int id) {
		updateTextViewHander.setText("您点击了按钮 ["+((Button)findViewById(id)).getText()+"]\n--------");
		updateHintHander.setText("提示:");
	}
	@Override
	public void onCreate(Bundle savedInstanceState ) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		updateTextViewHander = new UpdateTextViewHander(this, (TextView)findViewById(R.id.show_info));
		updateHintHander = new UpdateTextViewHander(this, (TextView)findViewById(R.id.show_hint));
		findViewById(R.id.get_binder).setOnClickListener(onClickListener);
		findViewById(R.id.get_other_proc_binder).setOnClickListener(onClickListener);
		findViewById(R.id.get_other_apk_binder).setOnClickListener(onClickListener);
		findViewById(R.id.release_binder).setOnClickListener(onClickListener);
		findViewById(R.id.fdtest).setOnClickListener(onClickListener);
		findViewById(R.id.send_greater1016kb).setOnClickListener(onClickListener);
		findViewById(R.id.send_less1016kb).setOnClickListener(onClickListener);
		findViewById(R.id.send_greater2M_image).setOnClickListener(onClickListener);
		findViewById(R.id.async_send_greater508kb).setOnClickListener(onClickListener);
		findViewById(R.id.async_send_less508kb).setOnClickListener(onClickListener);
		findViewById(R.id.get_thread_num).setOnClickListener(onClickListener);
		findViewById(R.id.add_thread).setOnClickListener(onClickListener);
		findViewById(R.id.test_exception).setOnClickListener(onClickListener);
		findViewById(R.id.oom_error).setOnClickListener(onClickListener);
		findViewById(R.id.noclassdeffounderror).setOnClickListener(onClickListener);
		findViewById(R.id.sleep_10s).setOnClickListener(onClickListener);
		findViewById(R.id.async_sleep_10s).setOnClickListener(onClickListener);
		findViewById(R.id.death_notice).setOnClickListener(onClickListener);
//		findViewById(R.id.intent_greater1016kb).setOnClickListener(onClickListener);
//		findViewById(R.id.intent_less1016kb).setOnClickListener(onClickListener);
		
		for( ClickHandler handler : ClickHandler.values()) {
			findViewById(handler.getId(this)).setOnClickListener(handler);
		}
	}
	
	DeathRecipient binderDeathRecipient = new DeathRecipient() {
		
		@Override
		public void binderDied() {
			updateTextViewHander.appendTextln("死亡通知：binder 已死亡。");
		}
	};
	ServiceConnection binderConnection = new ServiceConnection() {
		
		@Override
		public void onServiceDisconnected(ComponentName name) {
			updateTextViewHander.appendTextln("注销binder成功。");
			updateTextViewHander.appendTextln("   组件名:"+name);
			
		}
		
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			updateTextViewHander.appendTextln("创建binder成功。");
			updateTextViewHander.appendTextln("   组件名:"+name);
			updateTextViewHander.appendTextln("   Binder服务对象:"+service);
			updateTextViewHander.appendTextln("          获取到的是 【"+(service instanceof IDataSender.Stub?"Stub":"Proxy")+"】对象。");
			
			try {
				service.linkToDeath(binderDeathRecipient, 0 );
			} catch (RemoteException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			dataSender = IDataSender.Stub.asInterface(service);
			updateTextViewHander.appendTextln("   Binder对象:"+dataSender);
			updateHintHander.appendTextln("非本进程创建的binder对象，创建好后，能够拿到此对象的Proxy实例。\n如果是本进程的话，拿到的是Binder对象自身！从而本进程中使用Binder通信不会影响效率。");
		}
	};
	IDataSender dataSender = null;
	OnClickListener onClickListener = new OnClickListener() {
		@SuppressLint("NewApi") @Override
		public void onClick(View v) {

			Message message = new Message();
			initTextInfo(v.getId());
			switch (v.getId()) {
			case R.id.get_binder:
				if ( !checkBinderConnOk() ) {
					bindService(new Intent(MainActivity.this, BinderService.class), binderConnection, BIND_AUTO_CREATE);
				}
				break;
			case R.id.get_other_proc_binder:
				if ( !checkBinderConnOk() ) {
					bindService(new Intent(MainActivity.this, OtherProcBinderService.class), binderConnection, BIND_AUTO_CREATE);
				}
				break;
			case R.id.get_other_apk_binder:
				if ( !checkBinderConnOk() ) {
					String apkName = "com.ddlx.test.binder.appendant";
					PackageManager pm = getPackageManager();
					try {
						pm.getApplicationInfo(apkName, 0);
					} catch (NameNotFoundException e) {
						updateTextViewHander.appendTextln("！！！此apk没有安装:"+apkName);
						updateTextViewHander.appendTextln("      请先安装。");
						return;
					}
					Intent intent = new Intent();
					intent.setComponent(new ComponentName(apkName, apkName+".service.BinderService"));
					boolean ret = bindService(intent, binderConnection, BIND_AUTO_CREATE);
					updateTextViewHander.appendTextln("ret:"+ret);
				}
				break;
			case R.id.release_binder:
				if ( dataSender !=null ) {
					updateTextViewHander.appendTextln("注销成功");
					dataSender.asBinder().unlinkToDeath(binderDeathRecipient, 0);
					dataSender = null;
					unbindService(binderConnection);
				}
				break;
			case R.id.fdtest:
				if (!checkDataSenderInited() ) {
					return;
				}
				File file = new File("/data/data/com.ddlx.test.binder/files/binder/test.txt");
				BufferedWriter writer = null;
				try {
					if ( !file.exists() ) {
						file.getParentFile().mkdirs();
					}
					writer = new BufferedWriter(new FileWriter(file));
					writer.write("BINDER_FD_TEST.");
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} finally {
					if ( writer != null ) {

						try {
							writer.close();
						} catch (IOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}
				FileInputStream inputStream = null;
				try {
					inputStream = new FileInputStream(file);
					message.setFdData(inputStream.getFD());
					try {
						Result result = dataSender.sendFD(message);
						updateTextViewHander.appendTextln("测试完成。结果为:\n"+result);
						} catch (RemoteException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} finally {
					if ( inputStream != null ) {
						try {
							inputStream.close();
						} catch (IOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}
				updateHintHander.appendTextln("Binder FD传递时可以避开文件权限检查。\n因而可以向别的apk传递本apk的私有文件。");
				
				break;
			case R.id.send_greater1016kb:
				if (!checkDataSenderInited() ) {
					return;
				}
				message.setBytesData(new byte[1016*1024]);
				try {
					Result result = dataSender.sendData(message);
					updateTextViewHander.appendTextln("同进程Binder总是能通过 此项测试\n如果代码被厂商修改，也能通过此测试。\n(如红米手机也能通过此测试。是因为红米手机把此值由1016KB改成了2040KB了！)");
				} catch (TransactionTooLargeException e) {
					updateTextViewHander.appendTextln("抛出异常:\n"+e.toString());
				} catch (RemoteException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} 
				updateHintHander.appendTextln("Binder传输有大小的限制,限制为1016KB.\n超过此大小将无法传输,并抛出TransactionTooLargeException异常。");
				
				break;
			case R.id.send_less1016kb:
				if (!checkDataSenderInited() ) {
					return;
				}
				message.setBytesData(new byte[1015*1024]);
				try {
					Result result = dataSender.sendData(message);
					updateTextViewHander.appendTextln("运行结果:\n"+result);
				} catch (RemoteException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} 
				updateHintHander.appendTextln("Binder传输有大小的限制,限制为1016KB.\n"+
						"注意：因为1016KB是binder的总传输大小。如果在同一时间有多个BInder通信过程，则他们传输的数据总大小不能超过1016KB");
				
				break;
			case R.id.send_greater2M_image:
				if (!checkDataSenderInited() ) {
					return;
				}
				message.setBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.test_2m_image));
				try {
					Result result = dataSender.sendData(message);
					updateTextViewHander.appendTextln("同进程Binder总是能通过 此项测试");
				} catch (TransactionTooLargeException e) {

					updateTextViewHander.appendTextln("抛出异常:\n"+e.toString());
				}catch (RemoteException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

				updateHintHander.appendTextln("Bitmap使用Binder传输时不同系统表现不一样.\n"+
						"android4.0以上的系统，当图片大小超过40KB时会通过创建一个ashmem对象，按照传文件的方式传输\n"+
						"android4.0以下的系统，则直接当做数据块传递。超过1016kb就会报TransactionTooLargeException。");
				break;
			case R.id.async_send_greater508kb:
				if (!checkDataSenderInited() ) {
					return;
				}
				message.setBytesData(new byte[508*1024]);
				try {
					dataSender.postData(message, new IPostCallback.Stub() {
						
						@Override
						public void onResult(Result res) throws RemoteException {
							updateTextViewHander.appendTextln("同进程Binder总是能通过 此项测试\n如果代码被厂商修改，也能通过此测试。\n(如红米手机也能通过此测试。是因为红米手机把此值由1016KB改成了2040KB了！)");
						}
					});
				} catch (TransactionTooLargeException e) {
					updateTextViewHander.appendTextln("抛出异常:\n"+e.toString());

				} catch (RemoteException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				updateHintHander.appendTextln("使用binder异步通信时。限制为508KB.\n"+
						"超过此大小将无法传输,并抛出TransactionTooLargeException异常。");
				break;
			case R.id.async_send_less508kb:
				if (!checkDataSenderInited() ) {
					return;
				}
				message.setBytesData(new byte[507*1024]);
				try {
					dataSender.postData(message, new IPostCallback.Stub() {
						@Override
						public void onResult(Result res) throws RemoteException {
							updateTextViewHander.appendTextln("运行结果:\n"+res);
						}
					});
				} catch (RemoteException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} 
				updateHintHander.appendTextln("使用binder异步通信时,限制为508KB.\n"+
						"注意：因为508KB是binder的异步传输可用总大小。如果在同一时间有多个异步通信，则他们传输的数据总大小不能超过508KB");
				
				break;
			case R.id.get_thread_num:
				if (!checkDataSenderInited() ) {
					return;
				}
				updateTextViewHander.appendTextln("此测试需要半分钟的时间请耐心等待");
				final PackageManager packageManager = getPackageManager();
				final HashSet<Thread> binderThreads = new HashSet<Thread>();
				try {
					Method method = packageManager.getClass().getMethod("getPackageSizeInfo", String.class, IPackageStatsObserver.class );
					
					final int maxCount = 30;
					
					for (int i = 0; i < maxCount; i++) {
						final int a = i;
						try {
							method.invoke( packageManager, "com.ddlx.test.binder", new IPackageStatsObserver.Stub(){
								@Override
								public void onGetStatsCompleted(android.content.pm.PackageStats arg0, boolean arg1){
									binderThreads.add(Thread.currentThread());
									try {
										Thread.sleep(1000);
									} catch (InterruptedException e) {
										// TODO Auto-generated catch block
										e.printStackTrace();
									}
									if ( a == maxCount -1 ) {

										updateTextViewHander.appendTextln(">>>总共有("+binderThreads.size()+")个Binder线程。<<<");
										updateTextViewHander.appendTextln("-----\n以下是所有的binder线程名\n-----");
										for ( Thread thread : binderThreads ) {
											updateTextViewHander.appendTextln(thread.toString());
										}
									}
								}
							});
						} catch (InvocationTargetException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
					
				} catch (NoSuchMethodException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IllegalArgumentException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

				updateHintHander.appendTextln("默认最多Binder线程是16个,可以通过别的方式增加线程个数.\n"+
						"但这16个线程并不是一开始就全部创建,而是会按照需要创建.\n");
				break;
			case R.id.add_thread:
				if (!checkDataSenderInited() ) {
					return;
				}
				new Thread(new Runnable() {
					
					@Override
					public void run() {
						BinderInternal.joinThreadPool();
					}
				}, "MyBinderThread" ).start();
				
				updateTextViewHander.appendTextln("增加一个新的binder线程。");
				updateHintHander.appendTextln("可以使用\n"+
						"BinderInternal.joinThreadPool()\n"+
						"函数把当前线程变成Binder线程。");
				
				break;
			case R.id.test_exception:
				if (!checkDataSenderInited() ) {
					return;
				}
				Class<?> clss[] = {
						//不会被捕捉，会被直接忽略的异常(部分)
						RuntimeException.class,
						ActivityNotFoundException.class,
						NoSuchElementException.class,
						IndexOutOfBoundsException.class,
						//以下是可以被捕捉的异常
						NullPointerException.class,	
						SecurityException.class,
						BadParcelableException.class,
						IllegalArgumentException.class,
						IllegalStateException.class,
						NetworkOnMainThreadException.class,
						DdlxSecurityException.class,
				};
				for( int i=0; i<clss.length; i++ ) {
					try {
						dataSender.triggerException(clss[i].getName());

						updateTextViewHander.appendTextln("未抛出异常:"+clss[i].getName());
					} catch (Throwable e) {
						updateTextViewHander.appendTextln("抓到异常:"+e.toString()+"("+clss[i].getName()+")");
					}
				}
				
				
				updateHintHander.appendTextln(
						"只有以下异常(或子类)才能传递到客户端，其他的异常将会被忽略。\n"+
						"SecurityException\n"+
						"BadParcelableException\n"+
						"IllegalArgumentException\n"+
						"NullPointerException\n"+
						"IllegalStateException\n"+
						"NetworkOnMainThreadException\n");
				break;
			case R.id.oom_error:
				if (!checkDataSenderInited() ) {
					return;
				}
				try {
					dataSender.triggerException(OutOfMemoryError.class.getName());

					updateTextViewHander.appendTextln("未抛出异常");
				} catch (RemoteException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch ( OutOfMemoryError e ) {
					updateTextViewHander.appendTextln("抓到OutOfMemoryError异常。");
				}
				
				updateHintHander.appendTextln("OOM错误不会被客户端抓到。被服务端直接忽略。\n");
				break;
			case R.id.noclassdeffounderror:
				if (!checkDataSenderInited() ) {
					return;
				}
				try {
					dataSender.triggerException(NoClassDefFoundError.class.getName());

					updateTextViewHander.appendTextln("未抛出异常");
				} catch (RemoteException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					updateTextViewHander.appendTextln("抓到RemoteException异常。"+e.toString());
				} catch ( NoClassDefFoundError e ) {
					updateTextViewHander.appendTextln("抓到NoClassDefFoundError异常。");
				} 
				
				updateHintHander.appendTextln("非OOM的异常也不会传递到客户端。但是服务端会被强制终止\n");
				break;
			case R.id.sleep_10s:
				if (!checkDataSenderInited() ) {
					return;
				}
				try {
					updateTextViewHander.appendTextln("call sleep 10s.");
					dataSender.sleep(10*1000);
					updateTextViewHander.appendTextln("call sleep 10s. OK.");
				} catch (RemoteException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					updateTextViewHander.appendTextln("抓到RemoteException异常。"+e.toString());
				} 
				
				updateHintHander.appendTextln("同步调用，如果耗时太长会导致客户端当前线程挂起\n");
				break;
			case R.id.async_sleep_10s:
				if (!checkDataSenderInited() ) {
					return;
				}
				try {
					updateTextViewHander.appendTextln("call asyncSleep 10s.");
					dataSender.asyncSleep(10*1000);
					updateTextViewHander.appendTextln("call asyncSleep 10s. OK.");
				} catch (RemoteException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					updateTextViewHander.appendTextln("抓到RemoteException异常。"+e.toString());
				} 
				
				updateHintHander.appendTextln("异步调用不用等待服务端是否处理完成就直接返回了\n");
				break;
				
			case R.id.death_notice:
				if (!checkDataSenderInited() ) {
					return;
				}
				try {
					dataSender.kill(1);
				} catch (RemoteException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					updateTextViewHander.appendTextln("抓到RemoteException异常。"+e.toString());
				} 
				
				updateHintHander.appendTextln("注册了死亡通知在服务器退出的时候能够收到通知\n");
				break;
			default:
				break;
			}
		}
	};
}
